package com.analyse.stock.component;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.analyse.stock.form.PickStockQueryForm;
import com.analyse.stock.form.StrategyParamForm;
import com.analyse.stock.form.TradingForm;
import com.analyse.stock.model.StockCoreInfoModel;
import com.analyse.stock.model.StockKlineDailyModel;
import com.analyse.stock.model.UserPositionInfoModel;
import com.analyse.stock.utils.StockAnalyzer;
import lombok.extern.slf4j.Slf4j;
import org.ta4j.core.BarSeries;
import org.ta4j.core.Rule;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public abstract class TradingBizAbstract<R> {
    public TradingForm tradingForm;

    public TradingBizAbstract(TradingForm tradingForm) {
        this.tradingForm = tradingForm;
    }

    /**
     * 校验参数
     * 校验是否可以交易
     */
    public abstract boolean verify(Date now);
    /**
     * 校验是否今日交易 闭市
     */
    public abstract boolean isTrading(Date now);

    /**
     * 获取登录用户持仓数据
     **/
    public abstract UserPositionInfoModel getUserPositionData(String userId, String accountId, String stockCode);

    /**
     * 获取历史日线数据
     **/
    public abstract List<StockKlineDailyModel> getKlineData(PickStockQueryForm conditionForm);

    /**
     * 拉取股票实时数据,秒级别
     **/
    public abstract StockCoreInfoModel pullRealTimeData(String stockCode);

    /**
     * 执行卖出动作，挂委卖单
     **/
    public abstract R execSellRule(BarSeries barSeries,UserPositionInfoModel positionData,
                                   List<Rule> sellRule,StockCoreInfoModel realTimeData);

    /**
     * 执行买入动作，挂委买单
     **/
    public abstract R execBuyRule(BarSeries barSeries,UserPositionInfoModel positionData,
                                  List<Rule> buyRule,StockCoreInfoModel realTimeData);

    /**
     * 11.30分前早盘：
     * 计算一次，给早上 上证指数，深证成指，创业板指，科创50 情绪打分
     * 11.30分前 只【做空】
     *
     * 13.00后下午盘：
     * 计算一次，根据指数情绪和A股涨跌家数，主力净流入资金是否增加，两市总成交额是否缩量/放量
     * 情绪为(积极和中性)时，下午的盘 13.00后的盘【做多】也【做空】，尾盘30分钟开板就【卖空】
     * 情绪为(消极)时，【不做多】，尾盘30分钟开板就 【卖空】
     */
    final public void exec()  {
        log.info("### 股票：{}--{}，启动实盘交易，登录用户：{}",
                tradingForm.getStockModel().getStockName(),tradingForm.getStockModel().getStockCode(),tradingForm.getUserId());
        //当前股票持仓信息
        UserPositionInfoModel positionData = this.getUserPositionData(tradingForm.getUserId(),
                tradingForm.getAccountId(),tradingForm.getStockModel().getStockCode());

        //策略参数
        String paramJson = tradingForm.getStrategyPool().getParamJson();
        StrategyParamForm strategyParam = JSONObject.parseObject(paramJson,StrategyParamForm.class);

        //历史日线数据
        List<StockKlineDailyModel> klineData = this.getKlineData(strategyParam.getConditionForm());
        BarSeries barSeries = StockAnalyzer.createSeries(klineData,"k-line");

        AtomicInteger num = new AtomicInteger(0);
        boolean isTrading = true;
        while(isTrading){
            Date now = DateUtil.date();
            isTrading = this.isTrading(now);
            if(!isTrading){
                log.info("### 股票：{}--{}, 实盘交易,当前时间：{} , 不在交易时间,今日交易结束，收盘... ###",
                        tradingForm.getStockModel().getStockName(),
                        tradingForm.getStockModel().getStockCode(),DateUtil.format(now, DatePattern.NORM_TIME_PATTERN));
                continue;
            }
            //不在交易时间，小休中
            boolean isSleep = this.verify(now);
            if(!isSleep){
                log.info("### 股票：{}--{}, 实盘交易,当前时间：{} , 不在交易时间,小休时间... ###",
                        tradingForm.getStockModel().getStockName(),tradingForm.getStockModel().getStockCode(),
                        DateUtil.format(now, DatePattern.NORM_TIME_PATTERN));
                try {
                    //休息30分钟
                    Thread.yield();
                    Thread.sleep(30*60*1000);
                } catch (InterruptedException e) {
                    log.error("### 股票：{}--{}, 实盘交易,当前时间：{},发生异常：{}",
                            tradingForm.getStockModel().getStockName(),tradingForm.getStockModel().getStockCode(),
                            DateUtil.format(now, DatePattern.NORM_TIME_PATTERN),e.getMessage(),e);
                }
                continue;
            }

            //5秒间隔 拉取当前股票 开盘价，最新价，最高价，最低价，涨跌幅，成交量，成交额 实时数据
            //拉取 主力资金净流入和流出 数据
            long time0 = System.currentTimeMillis();
            StockCoreInfoModel model = this.pullRealTimeData(tradingForm.getStockModel().getStockCode());
            log.info("###  股票：{}--{}，交易中，第【{}】次盯盘...，最新价：{}, 涨跌幅：{}%，换手率：{}%，耗时：{} ms",
                    tradingForm.getStockModel().getStockName(),tradingForm.getStockModel().getStockCode(),num.get(),
                    model.getPrice(),model.getUpDownPercent(),model.getExchangeRate(),(System.currentTimeMillis()-time0));
            /**
             * 一买一卖 规则
             * 有持仓则  监控卖出
             * 无持仓则  监控买入
             **/
            if(Objects.nonNull(positionData)){
                this.execSellRule(barSeries,positionData,strategyParam.getSellRule(),model);
                log.info("### 股票：{},执行【买入】策略：....",tradingForm.getStockModel().getStockCode());
            }else{
                //策略执行，判断买点和买点
                this.execBuyRule(barSeries,positionData,strategyParam.getBuyRule(),model);
                log.info("### 股票：{},执行【卖出】策略：....",tradingForm.getStockModel().getStockCode());
            }
            try {
                Thread.yield();
                //休息5 s
                Thread.sleep(5*1000);
            } catch (InterruptedException e) {
                log.error("### 股票：{}--{}, 实盘交易,当前时间：{},发生异常：{}",
                        tradingForm.getStockModel().getStockName(),tradingForm.getStockModel().getStockCode(),
                        DateUtil.format(now, DatePattern.NORM_TIME_PATTERN),e.getMessage(),e);
            }
            num.incrementAndGet();
        }
    }
}
